package com.biz.impl;

import com.biz.BillBiz;
import com.core.Result;
import com.core.ResultGenerator;
import com.dao.BillRefundDao;
import com.entity.model.Bill;
import com.entity.model.BillRefund;
import com.biz.BillRefundBiz;
import com.core.AbstractBiz;
import com.util.PayUtil;
import com.util.UUIDHexGenerator;
import com.util.webpage.PageData;
import com.wechat.mina.Constants;
import com.wechat.mina.RefundUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * Created by jc on 2019/12/18.
 */
@Service
@Transactional
public class BillRefundBizImpl extends AbstractBiz<BillRefund> implements BillRefundBiz {
    @Resource
    private BillRefundDao billRefundDao;

    @Autowired
    private BillBiz billBiz;

    @Autowired
    private BillRefundBiz billRefundBiz;

    private static final Logger LOGGER = Logger.getLogger(BillRefund.class);
    private int socketTimeout = 10000;// 连接超时时间，默认10秒
    private int connectTimeout = 30000;// 传输超时时间，默认30秒
    private static RequestConfig requestConfig;// 请求器的配置
    private static CloseableHttpClient httpClient;// HTTP请求器

    @Override
    public List<BillRefund> billRefundCancelList(PageData cond) {
        return billRefundDao.billRefundCancelList(cond);
    }

    @Override
    public List<BillRefund> billRefundCompleteList(PageData cond) {
        return billRefundDao.billRefundCompleteList(cond);
    }


    //具体的调用微信的退款接口
    public Result refund(Integer billId, Integer billRefundId) {
        String code = Constants.CODE_SUCCESS;//状态码
        String msg = Constants.REFUND_SUCCESS;//提示信息
        Map<String, String> data = new HashMap<String, String>();
        //String  orderId = getPara("orderId");

        try {
            if (StringUtils.isEmpty(billId.toString())) {
                code = Constants.CODE_ERROR;
                msg = Constants.MSG_NULL;
            } else {
                //退款到用户微信
                Bill bill = billBiz.findById(billId);

                String nonce_str = UUIDHexGenerator.generate();//随机字符串
                String today = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
                String no = PayUtil.createCode(8);

                data.put("appid", Constants.appID);
                data.put("mch_id", Constants.MCH_ID);
                data.put("nonce_str", nonce_str);
                data.put("sign_type", "MD5");
                data.put("out_trade_no", bill.getBillNo());//商户订单号
                data.put("out_refund_no", today + no);//商户退款单号
                data.put("total_fee", String.valueOf(Math.round(bill.getCustomerPayFee() * 100)));//支付金额，微信支付提交的金额是不能带小数点的，且是以分为单位,这边需要转成字符串类型，否则后面的签名会失败
                data.put("refund_fee", String.valueOf(Math.round(bill.getCustomerPayFee() * 100)));//退款总金额,订单总金额,单位为分,只能为整数
//                data.put("notify_url", Constants.NOTIFY_URL_REFUND);//退款成功后的回调地址
                String preStr = RefundUtil.createLinkString(data); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
                //MD5运算生成签名，这里是第一次签名，用于调用统一下单接口
                String mySign = RefundUtil.sign(preStr, Constants.key, "utf-8").toUpperCase();
                data.put("sign", mySign);

                //拼接统一下单接口使用的xml数据，要将上一步生成的签名一起拼接进去
                String xmlStr = postData(Constants.REFUND_PATH, RefundUtil.GetMapToXML(data)); //支付结果通知的xml格式数据
                System.out.println(xmlStr);
                Map notifyMap = RefundUtil.doXMLParse(xmlStr);
                if ("SUCCESS".equals(notifyMap.get("return_code"))) {
                    if ("SUCCESS".equals(notifyMap.get("result_code"))) {

                        BillRefund billRefund = billRefundBiz.findById(billRefundId);
                        billRefund.setRefundNo((String) notifyMap.get("refund_id"));
                        billRefund.setRefundFee(bill.getCustomerPayFee());
                        billRefund.setStatus(1);
                        billRefund.setIsDefault(0);
                        billRefundBiz.update(billRefund);

                    } else {
                        System.out.println("退款失败:原因" + notifyMap.get("return_msg"));
                        code = Constants.CODE_ERROR;
                        msg = (String) notifyMap.get("return_msg");
                    }
                } else {
                    System.out.println("退款失败:原因" + notifyMap.get("return_msg"));
                    code = Constants.CODE_ERROR;
                    msg = (String) notifyMap.get("return_msg");
                }
            }
        } catch (Exception e) {
            code = Constants.CODE_ERROR;
            msg = Constants.MSG_01;
            LOGGER.error(e.toString(), e);
        }
        Map<String, Object> jsonResult = new HashMap<String, Object>();
        jsonResult.put("code", code);
        jsonResult.put("msg", msg);
        jsonResult.put("data", data);
        return ResultGenerator.genSuccessResult(jsonResult);
    }

    /**
     * 加载证书
     */
    private static void initCert() throws Exception {
        // 证书密码，默认为商户ID
        String key = Constants.MCH_ID;
        // 商户证书的路径
        String path = Constants.CERT_PATH;

        // 指定读取证书格式为PKCS12
        KeyStore keyStore = KeyStore.getInstance("PKCS12");

        // 读取本机存放的PKCS12证书文件
        FileInputStream instream = new FileInputStream(new File(path));
        try {
            // 指定PKCS12的密码(商户ID)
            keyStore.load(instream, key.toCharArray());
        } finally {
            instream.close();
        }

        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build();

        // 指定TLS版本
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        // 设置httpclient的SSLSocketFactory
        httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
    }


    /**
     * 通过Https往API post xml数据
     *
     * @param url    API地址
     * @param xmlObj 要提交的XML数据对象
     * @return
     */
    public String postData(String url, String xmlObj) {
        // 加载证书
        try {
            initCert();
        } catch (Exception e) {
            e.printStackTrace();
        }
        String result = null;
        HttpPost httpPost = new HttpPost(url);
        // 得指明使用UTF-8编码，否则到API服务器XML的中文不能被成功识别
        StringEntity postEntity = new StringEntity(xmlObj, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.setEntity(postEntity);
        // 根据默认超时限制初始化requestConfig
        requestConfig = RequestConfig.custom()
                .setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectTimeout)
                .build();
        // 设置请求器的配置
        httpPost.setConfig(requestConfig);
        try {
            HttpResponse response = null;
            try {
                response = httpClient.execute(httpPost);
            } catch (IOException e) {
                e.printStackTrace();
            }
            HttpEntity entity = response.getEntity();
            try {
                result = EntityUtils.toString(entity, "UTF-8");
            } catch (IOException e) {
                e.printStackTrace();
            }
        } finally {
            httpPost.abort();
        }
        return result;
    }
}
